package com.luo.camunda.app.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luo.camunda.app.constants.PaymentProcessConstants;
import com.luo.camunda.app.enums.ConfirmResultEnum;
import com.luo.camunda.app.enums.PaymentResultEnum;
import com.luo.camunda.app.mapper.BizPaymentProcessInfoMapper;
import com.luo.camunda.app.model.entity.BizPaymentProcessInfo;
import com.luo.camunda.app.model.param.PaymentConfirmParam;
import com.luo.camunda.app.model.param.PaymentQueryParam;
import com.luo.camunda.app.model.param.PaymentRequestParam;
import com.luo.camunda.app.model.wrapper.PaymentProcessVariablesWrapper;
import com.luo.camunda.app.service.IBizPaymentProcessInfoService;
import com.luo.camunda.app.utils.CommonUtils;
import com.luo.camunda.common.model.param.ProcessVariablesQueryParam;
import com.luo.camunda.common.model.param.TaskQueryParam;
import com.luo.camunda.common.model.vo.TaskVo;
import com.luo.camunda.common.servcie.CamundaCommonService;
import com.luo.camunda.common.utils.CamundaUtils;
import com.luo.demo.sc.base.model.result.RespResult;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 支付流程 - 业务数据 服务实现类
 *
 * @author luohq
 * @since 2022-01-30
 */
@SuppressWarnings("ALL")
@Service
@Slf4j
public class BizPaymentProcessInfoServiceImpl extends ServiceImpl<BizPaymentProcessInfoMapper, BizPaymentProcessInfo> implements IBizPaymentProcessInfoService {

    @Resource
    private CamundaCommonService camundaCommonService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RespResult<String> startPaymentProcess(PaymentRequestParam paymentRequestParam) {
        /** 插入支付流程业务数据 */
        BizPaymentProcessInfo bizPaymentProcessInfo = new BizPaymentProcessInfo();
        //拷贝参数中的属性：productName, productPrice, paymentAssignee
        BeanUtils.copyProperties(paymentRequestParam, bizPaymentProcessInfo);
        //设置业务数据默认状态
        bizPaymentProcessInfo.setPaymentResult(PaymentResultEnum.NOT_PAY.getCode());
        bizPaymentProcessInfo.setApprovalResult(ConfirmResultEnum.NOT_CONFIRM.getCode());
        log.info("保存支付流程业务数据，参数：{}", paymentRequestParam);
        Boolean result = this.save(bizPaymentProcessInfo);
        log.info("保存支付流程业务数据，结果：{}", result);
        Long id = bizPaymentProcessInfo.getId();
        if(id == null || id <= 0){
            log.error("业务主键为空");
            return RespResult.failed("业务主键为空");
        }
        String bizKey = String.valueOf(id);
        /** 启动支付流程 */
        log.info("开启支付流程处理，processKey：{}, businessKey：{}", PaymentProcessConstants.PAYMENT_PROCESS_ID, bizPaymentProcessInfo.getId());
        ProcessInstance processInstance = this.camundaCommonService.startProcessInstance(
                PaymentProcessConstants.PAYMENT_PROCESS_ID,
                String.valueOf(bizPaymentProcessInfo.getId()),
                paymentRequestParam);
       /* ProcessInstance processInstance = this.camundaCommonService.startProInsForBizKey(
                PaymentProcessConstants.PAYMENT_PROCESS_ID,
                String.valueOf(bizPaymentProcessInfo.getId()),
                paymentRequestParam);*/
        
        log.info("开启支付流程处理，结果：{}", processInstance);


        /** 更新业务数据中的流程实例ID */
        result = this.updateById(BizPaymentProcessInfo.builder()
                .processInstanceId(processInstance.getId())
                .id(bizPaymentProcessInfo.getId())
                .build());
        log.info("更新支付流程业务数据 - 流程实例ID，结果：{}", result);
        return RespResult.successData(bizPaymentProcessInfo.getProcessInstanceId());
    }

    @Override
    public RespResult<TaskVo<BizPaymentProcessInfo>> queryTasks(TaskQueryParam taskQueryParam) {
        log.info("查询待处理任务列表，参数：{}", taskQueryParam);
        RespResult<TaskVo<BizPaymentProcessInfo>> respResult = this.camundaCommonService.queryRuntimeTasks(taskQueryParam, (bizKey) -> {
            return this.getById(Long.valueOf(bizKey));
        });
        log.info("查询待处理任务列表，结果：{}", respResult);
        return respResult;
    }

    @Override
    public RespResult<HistoricTaskInstance> queryHistoryTasks(TaskQueryParam taskQueryParam) {
        return this.camundaCommonService.queryHistoryTasks(taskQueryParam);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RespResult confirmPayment(PaymentConfirmParam req) {
        log.info("用户确认支付, 参数：{}", req);
        //TODO 校验核实当前任务 是否属于当前登录人  通过任务ID 查询出此任务明细信息，比对任务归属人是否和执行人一致
        String exeUser = req.getAssignee();
        //暂不考虑多人 ,候选组的情况。当前仅考虑待办归属 assignee的情况
        Task currTask = this.camundaCommonService.queryTaskById(req.getTaskId());
        if(currTask == null){
            log.error("执行失败，待办任务不存在:{}",req.getTaskId());
            return null;
        }
        if(StrUtil.isNotEmpty(exeUser) && exeUser.equalsIgnoreCase(currTask.getAssignee())){
        }else{
            log.error("非法执行待办任务:{},exeUser:{},assignee:{}",req.getTaskId(),exeUser,currTask.getAssignee());
            return null;
        }
        
        /** 查询当前流程实例已经存在的流程变量 */
        ProcessVariablesQueryParam processVariablesQueryParam = ProcessVariablesQueryParam.builder()
                //.taskId(paymentConfirmParam.getTaskId())
                .processInstanceId(req.getProcessInstanceId())
                .build();
        //读取 流程实例对应的业务变量
        Map<String, Object> existProcessVariables = this.camundaCommonService.getRuntimeProcessVariables(processVariablesQueryParam);
        log.info("获取当前已存在流程变量，结果：{}", existProcessVariables);
        //使用流程变量包装器（便于获取属性）
        PaymentProcessVariablesWrapper existPaymentProcessVariablesWrapper = new PaymentProcessVariablesWrapper(existProcessVariables);

        /** 更新业务流程数据（更新范围：之前流程添加的 + 当前操作添加的 流程变量 ） */
        BizPaymentProcessInfo bizPaymentProcessInfo = BizPaymentProcessInfo.builder()
                .productDiscount(existPaymentProcessVariablesWrapper.getProductDiscount())
                .productDiscountPrice(existPaymentProcessVariablesWrapper.getProductDiscountPrice())
                
                .approvalResult(req.getApprovalResult())
                .approvalTime(LocalDateTime.now())
                .id(Long.valueOf(req.getBizKey()))
                .build();
        log.info("更新用户确认信息，参数：{}", bizPaymentProcessInfo);
        Boolean result = this.updateById(bizPaymentProcessInfo);
        log.info("更新用户确认信息，结果：{}", result);


        /** 更新流程引擎（完成用户确认任务 + 添加新的流程变量（用于后续网关判断）） */
        Map<String, Integer> processVariables = CamundaUtils.convertProcessVariablesFromPair(PaymentProcessConstants.PAYMENT_APPROVAL_RESULT_VAR_NAME, req.getApprovalResult());
        this.camundaCommonService.completeTask(req.getTaskId(), processVariables);

        return RespResult.success();
    }

    @Override
    public RespResult<BizPaymentProcessInfo> queryPayments(PaymentQueryParam paymentQueryParam) {
        IPage<BizPaymentProcessInfo> pageResult = this.page(
                CommonUtils.convertPage(paymentQueryParam),
                Wrappers.<BizPaymentProcessInfo>lambdaQuery().eq(null != paymentQueryParam.getId(), BizPaymentProcessInfo::getId, paymentQueryParam.getId())
                        .eq(null != paymentQueryParam.getProcessInstanceId(), BizPaymentProcessInfo::getProcessInstanceId, paymentQueryParam.getProcessInstanceId())
                        .like(null != paymentQueryParam.getProductName(), BizPaymentProcessInfo::getProductName, paymentQueryParam.getProductName())

                        .ge(null != paymentQueryParam.getProductPriceStart(), BizPaymentProcessInfo::getProductPrice, paymentQueryParam.getProductPriceStart())
                        .le(null != paymentQueryParam.getProductPriceEnd(), BizPaymentProcessInfo::getProductPrice, paymentQueryParam.getProductPriceEnd())

                        .ge(null != paymentQueryParam.getProductDiscountStart(), BizPaymentProcessInfo::getProductDiscount, paymentQueryParam.getProductDiscountStart())
                        .le(null != paymentQueryParam.getProductDiscountEnd(), BizPaymentProcessInfo::getProductDiscount, paymentQueryParam.getProductDiscountEnd())

                        .ge(null != paymentQueryParam.getProductDiscountPriceStart(), BizPaymentProcessInfo::getProductDiscountPrice, paymentQueryParam.getProductDiscountPriceStart())
                        .le(null != paymentQueryParam.getProductDiscountPriceEnd(), BizPaymentProcessInfo::getProductDiscountPrice, paymentQueryParam.getProductDiscountPriceEnd())


                        .eq(null != paymentQueryParam.getApprovalResult(), BizPaymentProcessInfo::getApprovalResult, paymentQueryParam.getApprovalResult())
                        .eq(null != paymentQueryParam.getPaymentResult(), BizPaymentProcessInfo::getPaymentResult, paymentQueryParam.getPaymentResult())
                        .eq(null != paymentQueryParam.getPaymentAssignee(), BizPaymentProcessInfo::getPaymentAssignee, paymentQueryParam.getPaymentAssignee())

                        .ge(null != paymentQueryParam.getCreateTimeStart(), BizPaymentProcessInfo::getCreateTime, paymentQueryParam.getCreateTimeStart())
                        .le(null != paymentQueryParam.getCreateTimeEnd(), BizPaymentProcessInfo::getCreateTime, paymentQueryParam.getCreateTimeEnd())

                        .ge(null != paymentQueryParam.getApprovalTimeStart(), BizPaymentProcessInfo::getApprovalTime, paymentQueryParam.getApprovalTimeStart())
                        .le(null != paymentQueryParam.getApprovalTimeEnd(), BizPaymentProcessInfo::getApprovalTime, paymentQueryParam.getApprovalTimeEnd())

                        .ge(null != paymentQueryParam.getPaymentTimeStart(), BizPaymentProcessInfo::getPaymentTime, paymentQueryParam.getPaymentTimeStart())
                        .le(null != paymentQueryParam.getPaymentTimeEnd(), BizPaymentProcessInfo::getPaymentTime, paymentQueryParam.getPaymentTimeEnd())
        );
        return CommonUtils.convertPageResult(pageResult);
    }
    
    @Override
    public RespResult getDeployedStartForm(String id) {
        
        String result = this.camundaCommonService.getDeployedStartForm(id);
        return RespResult.successData(result);
    }
    
    @Override
    public RespResult getDeployedTaskForm(String taskId) {
        String result = this.camundaCommonService.getDeployedTaskForm(taskId);
        return RespResult.successData(result);
    }
    
}
